use anyhow::Result;
// use sqlx::postgres::
use chrono::{DateTime, Utc};
use uuid::Uuid;

use sqlx::{postgres::PgPoolOptions, Pool, Postgres, Row};
use tokio_stream::StreamExt;
// use crate::feed::Feed;

use crate::feed::{AtomOrRss, Feed};
#[derive(Clone)]
pub struct Db {
    pool: Pool<Postgres>,
    // connection: Connection<Socket, NoTlsStream>
}
impl Db {
    /// Connect to postgres database
    pub async fn connect(postgres_url: &str) -> Result<Self> {
        let pool = PgPoolOptions::new()
            .max_connections(10)
            .connect(postgres_url)
            .await?;
        Ok(Self { pool })
    }
    pub async fn get_feeds(&self) -> Result<Vec<Feed>> {
        let mut data = sqlx::query("SELECT * from feeds").fetch(&self.pool);
        // .await?;
        // println!("{:#?}", data);
        let mut feeds = Vec::new();
        while let Some(row) = data.try_next().await? {
            // let item = item?;
            let id = row.try_get(3)?;
            let last_update_time = row.try_get(2)?;
            let url = row.try_get(0)?;
            let feed_type: String = row.try_get(1)?;
            let feed_type = feed_type.parse::<AtomOrRss>()?;
            let item = Feed::new(id, last_update_time, url, feed_type);
            println!("{:#?}", item);
            feeds.push(item);
        }
        Ok(feeds)
    }
    pub async fn add_feed(&self, feed: Feed) -> Result<()> {
        sqlx::query(
            "Insert into feeds (id, last_update_time, url, feed_type) values ($1, $2, $3, $4)",
        )
        .bind(feed.id)
        .bind(feed.last_update_time)
        .bind(feed.url)
        .bind(feed.feed_type.to_string())
        .execute(&self.pool)
        .await?;
        Ok(())
    }
    pub async fn get_token(&self) -> Result<Option<String>> {
        let data = sqlx::query("Select * from tokens")
            .fetch_optional(&self.pool)
            .await?
            .map(|a| a.get(0));
        Ok(data)
    }
    // pub async fn set_token(&self, token: String) -> Result<()>{
    //     sqlx::query("Insert into tokens $1")
    //     .bind(token)
    //     .execute(&self.pool).await?;
    //    Ok(())
    // }
    pub async fn update_token(&self, token: String) -> Result<()> {
        sqlx::query("Update tokens set matrix = $1")
            .bind(token)
            .execute(&self.pool)
            .await?;
        Ok(())
    }
    pub async fn update_update_time(&self, update_time: DateTime<Utc>, id: Uuid) -> Result<()> {
        sqlx::query("Update feeds set last_update_time = $1 where id = $2")
            .bind(update_time)
            .bind(id)
            .execute(&self.pool)
            .await?;
        Ok(())
    }
}
